package com.gzyj.jtt808.v2011;

import com.gzyj.jtt808.utils.HexBytesUtil;
import io.netty.buffer.*;
import lombok.Data;

import java.util.ArrayList;
import java.util.List;

/**
 * @author lhj
 */
@Data
public abstract class Package {

    protected static final short HEAD_FLAG = 0x7e;

    private PackageHeader packageHeader;

    private Byte checkCode;

    public ByteBuf SubDataBodies;//分包数据体


    protected static final short END_FLAG = 0x7e;


    public Package() {

    }

    public Package(int msgId) {
        this.packageHeader = new PackageHeader(msgId);
    }

    public ByteBuf encode() {
        ByteBuf bodyBuf = Unpooled.buffer();
        if (this.getPackageHeader().getMsgBodyProperty().isSubcontract()) {

            ByteBuf tt = Unpooled.buffer();
            encodeImpl(tt);
            this.SubDataBodies = Unpooled.copiedBuffer(tt);

            bodyBuf.writeBytes(this.SubDataBodies);

        } else {
            // 写入数据体
            encodeImpl(bodyBuf);
        }
        this.packageHeader.getMsgBodyProperty().setDataLength(bodyBuf.readableBytes());

        ByteBuf headerBuf = this.packageHeader.encode();

        CompositeByteBuf tmp = Unpooled.compositeBuffer();
        tmp.addComponents(true, headerBuf, bodyBuf);

        ByteBuf finalBuffer = Unpooled.copiedBuffer(tmp);
        byte checkCode = bcc(finalBuffer, 0);
        finalBuffer.writeByte(checkCode);

        //转义
        byte[] bytes = Unpooled.copiedBuffer(finalBuffer).array();
        ByteBuf headFormatedBuffer = Unpooled.buffer(finalBuffer.readableBytes());
        formatBuffer(bytes, headFormatedBuffer);
        ByteBuf buffera = Unpooled.buffer(headFormatedBuffer.readableBytes() + 2);
        buffera.writeByte(HEAD_FLAG);
        buffera.writeBytes(headFormatedBuffer);
        buffera.writeByte(END_FLAG);


        return buffera;


    }


    public Package decode(ByteBuf in) {

        String hex = ByteBufUtil.hexDump(in);

        ByteBuf out = unescape(in);

        packageHeader = new PackageHeader();

        try {
            packageHeader.decode(out);
        } catch (Exception e) {
            e.printStackTrace();
        }


        String hext = ByteBufUtil.hexDump(out);
        int len = (hext.length() - 2) / 2;
        byte[] t = HexBytesUtil.readByOffset(out, len);
        ByteBuf body = Unpooled.copiedBuffer(t);


        if (packageHeader.getMsgBodyProperty().isSubcontract()) {
            //有分包

            this.SubDataBodies = Unpooled.copiedBuffer(body); //HexBytesUtil.readByOffset(in,this.packageHeader.getMsgBodyProperty().getDataLength()-1);

        } else {
            decodeImpl(body);
        }

        byte[] temp = HexBytesUtil.readByOffset(out, 1);

        this.checkCode = temp[0];

        System.out.println("校验码" + Short.parseShort(HexBytesUtil.bytesToHex(new byte[]{this.checkCode}), 16));


        return this;


    }


    protected abstract void encodeImpl(ByteBuf buf);

    protected abstract Package decodeImpl(ByteBuf buf);

    /**
     * BCC校验(异或校验)
     */
    public byte bcc(ByteBuf byteBuf, int tailOffset) {
        byte cs = 0;
        int readerIndex = byteBuf.readerIndex();
        int writerIndex = byteBuf.writerIndex() + tailOffset;
        while (readerIndex < writerIndex)
            cs ^= byteBuf.getByte(readerIndex++);
        return cs;
    }

    /**
     * 报文转义
     *
     * @param bytes
     * @param formatBuffer
     */
    private void formatBuffer(byte[] bytes, ByteBuf formatBuffer) {
        for (byte b : bytes) {
            switch (b) {
                case 0x7e:
                    byte[] formatByte0x5b = new byte[2];
                    formatByte0x5b[0] = 0x7d;
                    formatByte0x5b[1] = 0x02;
                    formatBuffer.writeBytes(formatByte0x5b);
                    break;
                case 0x7d:
                    byte[] formatByte0x5a = new byte[2];
                    formatByte0x5a[0] = 0x7d;
                    formatByte0x5a[1] = 0x01;
                    formatBuffer.writeBytes(formatByte0x5a);
                    break;
                default:
                    formatBuffer.writeByte(b);
                    break;
            }
        }
    }

    private static final ByteBufAllocator ALLOC = PooledByteBufAllocator.DEFAULT;

    /**
     * 反转义
     */
    public ByteBuf unescape(ByteBuf source) {
        int low = source.readerIndex();
        int high = source.writerIndex();
        int last = high - 1;

        if (source.getByte(0) == 0x7e) {
            low = low + 1;
        }


        if (source.getByte(last) == 0x7e) {
            high = last;
        }


        int mark = source.indexOf(low, high, (byte) 0x7d);
        if (mark == -1) {
            return source.slice(low, high - low);
        }

        List<ByteBuf> bufList = new ArrayList<>(3);

        int len;
        do {

            len = mark + 2 - low;
            bufList.add(slice(source, low, len));
            low += len;

            mark = source.indexOf(low, high, (byte) 0x7d);
        } while (mark > 0);

        bufList.add(source.slice(low, high - low));
        CompositeByteBuf compositeByteBuf = new CompositeByteBuf(ALLOC, false, bufList.size(), bufList);
        /*String hex = "7e" + ByteBufUtil.hexDump(compositeByteBuf) + "7e";
        ByteBuf out = Unpooled.buffer();
        out.writeBytes(HexBytesUtil.hexTobytes(hex));*/
        return Unpooled.copiedBuffer(compositeByteBuf);
    }

    /**
     * 截取转义前报文，并还原转义位
     */
    private ByteBuf slice(ByteBuf byteBuf, int index, int length) {
        byte second = byteBuf.getByte(index + length - 1);
        if (second == 0x01) {
            return byteBuf.slice(index, length - 1);
        } else if (second == 0x02) {
            byteBuf.setByte(index + length - 2, 0x7e);
            return byteBuf.slice(index, length - 1);
        } else {
            return byteBuf;
        }
    }


}
